package com.globallogic.jira.gadget.composite;

import java.util.ArrayList;
import java.util.List;

import com.atlassian.jira.issue.Issue;

/**
 * @author anton.pereverziev
 * @author yegor.chumakov
 * 
 *         An n-ary tree structure, needed to represent a linked issues
 *         hierarchy
 */
public class ReportNode {

	private static String COLOR_TRANSPARENT = "transparent";
	private static String COLOR_RED = "red";
	private static String COLOR_GREEN = "green";
	private static String COLOR_YELLOW = "#FFA500;";
	private static String ROOT_DESCRIPTION = "Root element";

	private final Issue issue;
	private final List<ReportNode> children = new ArrayList<ReportNode>();
	/**
	 * is that node are considered as root of hierarchy node.
	 */
	private boolean root;
	private String linkType;
	private boolean overflow = false;

	private Long remainingEstimate = null;
	private Long originalEstimate = null;
	private Long workLogged = null;

	public ReportNode() {
		this.issue = null;
	}

	public ReportNode(final Issue issue) {
		this.issue = issue;
	}

	public static ReportNode createRootNode(final Issue issue) {
		final ReportNode node = new ReportNode(issue);
		node.linkType = ROOT_DESCRIPTION;
		return node;
	}

	public String getLoggedWorkColor() {
		String result = ReportNode.COLOR_TRANSPARENT;
		final Long wl = getBranchTimeSpent() == -1 ? 0 : getBranchTimeSpent();
		final Long re = getBranchRemainingEstimate();
		final Long oe = getBranchOriginalEstimate();
		if (re != 0 && oe != 0) {
			final Long se = wl + re;
			if (se > oe)
				result = ReportNode.COLOR_RED;
			else if (se > (oe * 0.9) && se <= oe)
				result = ReportNode.COLOR_YELLOW;
			else
				result = ReportNode.COLOR_GREEN;
		}
		return result;
	}

	public String getPreparedDescription() {
		final String result = issue.getDescription() == null ? "No description available"
				: issue.getDescription();

		return result.replace("\n", "<br/>").replace("\r", "");
	}

	public List<ReportNode> getChild() {
		return children;
	}

	public Issue getIssue() {
		return issue;
	}

	public void addNode(final ReportNode node) {
		children.add(node);

	}

	public List<ReportNode> getChildren() {
		return children;
	}

	public String getLinkType() {
		return linkType;
	}

	public void setLinkType(final String linkType) {
		this.linkType = linkType;
	}

	/**
	 * @see {@link #getResolvedNodesNumber()}
	 * @return
	 */
	public int getReadyStatusPersents() {
		return getResolvedNodesNumber() * 100 / (getTreeLength() + 1);
	}

	/**
	 * @see {@link #getResolvedNodesNumber()}
	 * @return
	 */
	public String getReadyStatusString() {
		return getResolvedNodesNumber() + "/" + (getTreeLength() + 1);
	}

	/**
	 * Return nodes which status are resolved
	 * 
	 * @return count of nodes resolved
	 */
	public int getResolvedNodesNumber() {
		int count = (issue.getStatusObject().getName().equals("Resolved")) ? 1
				: 0;
		for (final ReportNode node : children) {
			count += node.getResolvedNodesNumber();
		}
		return count;
	}

	public int getTreeLength() {
		int count = children.size();
		for (final ReportNode node : children) {
			count += node.getTreeLength();
		}
		return count;
	}

	public boolean isRoot() {
		return root;
	}

	public void setRoot(final boolean root) {
		this.root = root;
	}

	/**
	 * Converts time from seconds to string "x w x d x h x m" or returns
	 * "undefined" in case of parameter is null
	 * 
	 * @param sec
	 * @return
	 */
	protected String timeConvert(Long sec) {
		String result = " n/a ";
		if (sec != null && sec != 0) {
			result = "";
			sec /= 60;

			if (((sec % 240000) / 2400) != 0)
				result += ((sec % 240000) / 2400) + "w ";
			if (((sec % 2400) / 480) != 0)
				result += ((sec % 2400) / 480) + "d ";
			if (((sec % 480) / 60) != 0)
				result += ((sec % 480) / 60) + "h ";
			if (result.isEmpty() || (sec % 60) != 0)
				result += (sec % 60) + "m ";
		}
		return result;
	}

	public String getOrigialEstimate() {
		return timeConvert(getBranchOriginalEstimate());
	}

	private Long getBranchOriginalEstimate() {
		if (originalEstimate != null)
			return originalEstimate;
		originalEstimate = issue.getOriginalEstimate() == null ? 0 : issue
				.getOriginalEstimate();
		for (final ReportNode reportNode : children) {
			originalEstimate += reportNode.getBranchOriginalEstimate() == (-1l) ? 0
					: reportNode.getBranchOriginalEstimate();
		}
		return originalEstimate;
	}

	public String getRemainingEstimate() {
		return timeConvert(getBranchRemainingEstimate());
	}

	private Long getBranchRemainingEstimate() {
		if (remainingEstimate != null)
			return remainingEstimate;
		remainingEstimate = (issue.getEstimate() == null ? 0 : issue
				.getEstimate());
		for (final ReportNode reportNode : children) {
			remainingEstimate += (reportNode.getBranchRemainingEstimate() == (-1l) ? 0
					: reportNode.getBranchRemainingEstimate());
		}
		return remainingEstimate;
	}

	public String getLoggedWork() {
		return timeConvert(getBranchTimeSpent());
	}

	private Long getBranchTimeSpent() {
		if (workLogged != null)
			return workLogged;
		workLogged = issue.getTimeSpent() == null ? 0 : issue.getTimeSpent();
		for (final ReportNode reportNode : children) {
			workLogged += reportNode.getBranchTimeSpent() == (-1l) ? 0
					: reportNode.getBranchTimeSpent();
		}
		return workLogged;
	}

	public boolean isOverflow() {
		return overflow;
	}

	public void setOverflow(final boolean overflow) {
		this.overflow = overflow;
	}
}
